/*
 * \file TestTitledDataModel.cpp
 *
 * \date Created on: Feb 1, 2009
 * \author sarah
 */

#include "TestTitledDataModel.h"
#include <QByteArray>
#include <QPixmap>
#include <Models/TitledDataModel.h>

class DummyTitledData : public TitledData
{
public:
    DummyTitledData(const QString &title) : TitledData(title) { }
    virtual ~DummyTitledData() {}
    QString description() const { return "Dummy titled data description"; }
    void saveToXml() const {}
    void loadFromXml() {}
};

TestTitledDataModel::TestTitledDataModel()
{
}

TestTitledDataModel::~TestTitledDataModel()
{
}

void TestTitledDataModel::initTestCase()
{
}

void TestTitledDataModel::cleanupTestCase()
{
}

void TestTitledDataModel::modelSpecToId_data()
{
    QTest::addColumn<int>("category");
    QTest::addColumn<int>("entry");
    QTest::addColumn<int>("expected");
    QTest::newRow("two values") << 3 << 5 << ((3 << 16) | 5);
    QTest::newRow("hard two values") << 3 << 5 << 0x00030005;
    QTest::newRow("neg lo byte") << 5 << -1 << 0x0005ffff;
    QTest::newRow("neg hi byte") << -1 << 3 << int(0xffff0003);
}

void TestTitledDataModel::modelSpecToId()
{
    QFETCH(int, category);
    QFETCH(int, entry);
    QFETCH(int, expected);
    int got = TitledDataUtil::modelSpecToId(category, entry);
    QCOMPARE(got, expected);
}

void TestTitledDataModel::idToCategory_data()
{
    QTest::addColumn<int>("id");
    QTest::addColumn<int>("expected");
    QTest::newRow("single value") << (5 << 16) << 5;
    QTest::newRow("two values") << ((5 << 16) | 99) << 5;
    QTest::newRow("hard two values") << 0x00050063 << 5;
    QTest::newRow("neg value") << int(0xffffffff) << -1;
    QTest::newRow("neg lo byte") << 0x0005ffff << 5;
    QTest::newRow("neg hi byte") << int(0xffff0003) << -1;
}

void TestTitledDataModel::idToCategory()
{
    QFETCH(int, id);
    QFETCH(int, expected);
    int got = TitledDataUtil::idToCategory(id);
    QCOMPARE(got, expected);
}

void TestTitledDataModel::idToEntry_data()
{
    QTest::addColumn<int>("id");
    QTest::addColumn<int>("expected");
    QTest::newRow("single value") << 5 << 5;
    QTest::newRow("two values") << ((99 << 16) | 5) << 5;
    QTest::newRow("hard two values") << 0x00630005 << 5;
    QTest::newRow("neg value") << int(0x0000ffff) << -1;
    QTest::newRow("neg lo byte") << 0x0003ffff << -1;
    QTest::newRow("neg hi byte") << int(0xffff0005) << 5;
}

void TestTitledDataModel::idToEntry()
{
    QFETCH(int, id);
    QFETCH(int, expected);
    int got = TitledDataUtil::idToEntry(id);
    QCOMPARE(got, expected);
}

void TestTitledDataModel::indexByRowColParent()
{
    QVERIFY(qApp);
    DummyTitledData data("Dummy title");
    TitledDataModel model(&data);
    QModelIndex index = model.index(1, 0, QModelIndex());
    QCOMPARE(index.internalId(), Q_INT64_C(0x0001ffff));
    QCOMPARE(TitledDataUtil::idToCategory(index.internalId()), 1);
    QCOMPARE(TitledDataUtil::idToEntry(index.internalId()), -1);
    QModelIndex newIndex = model.index(1, 0, index);
    QCOMPARE(newIndex.internalId(), Q_INT64_C(0x00010001));
}

class RowColTestModel : public TitledDataModel
{
public:
    RowColTestModel(const TitledData *source, QObject *parent)
            : TitledDataModel(source, parent)
    {
        clear();
        addCategory("Zero", QIcon(":/icons/icons/16x16/actions/folder-new.png"), 1);
        addCategory("One", QIcon(":/icons/icons/16x16/actions/media-playback-start.png"), 2);
        addCategory("Two", QIcon(":/icons/icons/16x16/actions/format-justify-fill.png"), 3);
        addCategory("Three", QIcon(":/icons/icons/16x16/actions/format-justify-fill.png"), 4);
        addCategory("Four", QIcon(":/icons/icons/16x16/actions/format-justify-fill.png"), 0);
        addEntry(0, "Entry Zero A");
        addEntry(1, "Entry One A");
        addEntry(1, "Entry One B");
        addEntry(2, "Entry Two A");
        addEntry(2, "Entry Two B");
        addEntry(2, "Entry Two C");
        addEntry(3, "Entry Three A");
        addEntry(3, "Entry Three B");
        addEntry(3, "Entry Three C");
        addEntry(3, "Entry Three D");
    }
};

void TestTitledDataModel::rowCount()
{
    DummyTitledData data("Dummy title");
    RowColTestModel model(&data, this);

    // Five "categories" (rows) under the root node
    QCOMPARE(model.rowCount(), 5);

    // One "entry" (row) in the zero'th category
    QModelIndex category = model.index(0, 0, QModelIndex());
    QCOMPARE(model.rowCount(category), 1);

    // One "entry" (row) in the fourth category - even tho it has
    // no entries it itself has 1 row and 1 column.
    category = model.index(4, 0, QModelIndex());
    QCOMPARE(model.rowCount(category), 0);

    // Three "entries" (rows) in the second category
    category = model.index(2, 0, QModelIndex());
    QCOMPARE(model.rowCount(category), 3);

    // No rows under the entry, its a leaf
    category = model.index(3, 0, QModelIndex());
    QModelIndex leafEntry = model.index(3, 0, category);
    QVERIFY(leafEntry.isValid());
    QCOMPARE(model.rowCount(leafEntry), 0);
}

void TestTitledDataModel::columnCount()
{
    DummyTitledData data("Dummy title");
    RowColTestModel model(&data, this);

    // All entries have one column, but check anyway
    // Root node has one column for all under it
    QCOMPARE(model.columnCount(), 1);

    // Category zero has one column for all under it
    QModelIndex category = model.index(0, 0, QModelIndex());
    QCOMPARE(model.columnCount(category), 1);

    // Category four has one column for all under it
    category = model.index(4, 0, QModelIndex());
    QCOMPARE(model.columnCount(category), 1);

    // Entry 2 under category 3 has no columns.
    category = model.index(3, 0, QModelIndex());
    QModelIndex leafEntry = model.index(2, 0, category);
    QCOMPARE(model.columnCount(leafEntry), 0);
}

void TestTitledDataModel::data()
{
    DummyTitledData data("Dummy title");
    RowColTestModel model(&data, this);

    // Data for the zero'th category heading
    QModelIndex category = model.index(0, 0, QModelIndex());
    QCOMPARE(model.data(category, Qt::DisplayRole).toString(), QString("Zero"));
    QIcon iconGot = qvariant_cast<QIcon>(model.data(category, Qt::DecorationRole));
    QIcon iconExpected(":/icons/icons/16x16/actions/folder-new.png");
    QVERIFY(!iconGot.isNull());
    QPixmap pixmap = iconGot.pixmap(16, 16);
    QByteArray bytes;
    QBuffer buffer(&bytes);
    buffer.open(QIODevice::WriteOnly);
    pixmap.save(&buffer, "PNG");
    pixmap = iconExpected.pixmap(16, 16);
    QByteArray expBytes;
    QBuffer expBuffer(&expBytes);
    expBuffer.open(QIODevice::WriteOnly);
    pixmap.save(&expBuffer, "PNG");
    QCOMPARE(bytes, expBytes);

    // Data for the fourth category heading
    category = model.index(4, 0, QModelIndex());
    QCOMPARE(model.data(category, Qt::DisplayRole).toString(), QString("Four"));

    // Data for entry 3 under category 3
    category = model.index(3, 0, QModelIndex());
    QModelIndex leafEntry = model.index(3, 0, category);
    // This one causes a warning to be displayed
    // QCOMPARE(model.data(leafEntry, Qt::DisplayRole), QVariant());
    QCOMPARE(model.data(leafEntry, Qt::DecorationRole), QVariant());
}

void TestTitledDataModel::hasChildren()
{
    DummyTitledData data("Dummy title");
    RowColTestModel model(&data, this);

    // Root node has children
    QCOMPARE(model.hasChildren(), true);

    // Category zero has one child
    QModelIndex category = model.index(0, 0, QModelIndex());
    QCOMPARE(model.hasChildren(category), true);

    // Category four has no children
    category = model.index(4, 0, QModelIndex());
    QCOMPARE(model.hasChildren(category), false);

    // Entry 2 under category 3 has no children, its a leaf.
    category = model.index(3, 0, QModelIndex());
    QModelIndex leafEntry = model.index(2, 0, category);
    QCOMPARE(model.hasChildren(leafEntry), false);
}

void TestTitledDataModel::parent()
{
    DummyTitledData data("Dummy title");
    RowColTestModel model(&data, this);

    // Dont crash with invalid parent
    QCOMPARE(model.parent(QModelIndex()), QModelIndex());

    // Category zero has null parent - its top level
    QModelIndex category = model.index(0, 0, QModelIndex());
    QCOMPARE(model.parent(category), QModelIndex());

    // Entry 2 under category 3 has category 3 as parent.
    category = model.index(3, 0, QModelIndex());
    QModelIndex leafEntry = model.index(2, 0, category);
    QCOMPARE(model.parent(leafEntry), category);

    // Entry 0 under category 1 has category 1 as parent.
    category = model.index(1, 0, QModelIndex());
    leafEntry = model.index(0, 0, category);
    QCOMPARE(model.parent(leafEntry), category);
}
